#pragma once
#include<iostream>
#include<string>
#include<cerrno>
#include<cstring>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<pthread.h>
namespace Client
{
    enum 
    {
        USAGE_ERR = 1,
        SOCKET_ERR = 2,
        BIND_ERR
    };
    class udpClient
    {
    public:
        udpClient(const std::string &serverip, const uint16_t &serverport)
            : serverip_(serverip),serverport_(serverport),sockfd_(-1),quit_(false)
        {}
        void initClient()
        {
            //1.客户端要创建socket
            sockfd_ = socket(AF_INET,SOCK_DGRAM,0);
            if(sockfd_ == -1)
            {
                std::cerr << "socket error:" << errno << ":"<<strerror(errno)<<std::endl;
                exit(SOCKET_ERR);//创建失败，直接退出
            }
            std::cerr << "socket success: " << sockfd_ << std::endl;
            //2.client需不要绑定呢？以及client需不需要明确的绑定呢？
            //所谓的bind就是将套接字的文件信息和网络的ip与端口号产生关联！
            //服务端要进行bind！那么客户端呢？——也是必须要的！
            //因为只要进行网络通信，无论是服务端还是客户端都要有ip和端口号！
            //但是client端的是不用程序员自己去bind的！——也就是不用显式的去bind！
            //在server端如果我们不去bind，也有有操作系统帮我们做！
            //但是为什么我们还要去bind呢？——在服务端我们绑定的时候，最重要的不是绑定ip！最重要的是绑定端口！
            //为什么说绑定的端口很重要的呢？——因为服务器要明确的端口号！不能随意改变！所以要由用户指明！
            //只要这样！客户端才能通过端口号找到服务器！ 
            //而客户端需要端口！但是端口号是几不重要！——这个端口的唯一性更重要！
            //因为客户端需要向服务端发生信息！所以服务端的端口号必须指明！
            //而客户端只要自己有端口就可以！未来客户端要向服务端发送信息!只要客户端将自己的端口号填写在信息里面！保证服务端可以响应就可以！
            //更重要的时候写服务器的是一家公司！而写客户端的是很多家公司
            //像是抖音，所有的抖音服务器都是字节内部，都是一家公司的！
            //但是我们手机或者笔记本上肯定有很多的软件！每个软件都是来自不同的公司！
            //如果我们客户端都是固定的绑定了一个固定的端口号！如果此时这个端口号被另一个软件给占用了怎么办？——那么这个软件就无法启动了！公司之间可不会沟通说你用那个，我用那个！
            //所以客户端不需要明确是哪一个！只有有就可以的！——所以我们让操作系统帮我们随机分配一个来进行绑定就可以！这个端口号是多少不重要！重要的是唯一性即可！
            //所以UDP客户端只要创建套接字就可以了！这个只要不调用bind函数就可以了！
            
            //那么操作系统在什么时候进行绑定？

        }

        // void run()
        // {
        //     struct sockaddr_in serveraddr;
        //     memset(&serveraddr, 0, sizeof(serveraddr)); // 全部初始化为0
        //     serveraddr.sin_family = AF_INET;
        //     serveraddr.sin_port = htons(serverport_);
        //     serveraddr.sin_addr.s_addr = inet_addr(serverip_.c_str());

        //     std::string message;
        //     char cmdline[1024];
        //     //写一个多线程！——让一部分进程来进行发送！一部分进行来进行收取！
            
        //     while(!quit_)
        //     {
        //         //  std::cout << "Please Enter# ";
        //         //  std:: cin >> message;//这个读取会按照空格来进行分割读取！
        //          std::cout << "# ";
        //          fgets(cmdline,sizeof(cmdline),stdin);

        //          message = cmdline;
        //          message[message.size()-1] = 0;//去掉\n

        //          sendto(sockfd_,message.c_str(),message.size(),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
        //          //操作系统就是在这时候发下socket没有绑定ip和port那么这时候操作系统就会帮我们自动的绑定！

        //          char buffer[1024];
        //          struct sockaddr_in temp; 
        //          socklen_t temp_len = sizeof(temp);
        //          size_t n = recvfrom(sockfd_,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&temp,&temp_len);//为什么我们还要多一个接收服务器的结构体呢？
        //          //因为有时候客户端会做一些特别的处理，比如远端有几个服务器都是给我们提供一些相同的服务
        //          //一般我们使用的1号机器！但是有时候1号挂掉了！那么客户端会自动换成2号的！我们还是要知道到底是给我们发回来的！——只不过我们这里不用他！
        //          if(n >= 0)buffer[n] =0;//给最后的加上/0,要 >= 0才会清空内容！
        //         //  std::cout << "服务器的翻译结果是 : "<< buffer << std::endl;
        //         std::cout << buffer << std::endl;
            
        //     }
        // }
        static void* sendMessage(void* fd)
        {
            int sockfd = *(static_cast<int *>(fd));
            // pthread_detach(pthread_self());//进行线程分离
            while(true)
            {
                char buffer[1024];
                struct sockaddr_in temp;
                socklen_t temp_len = sizeof(temp);
                size_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &temp_len); // 为什么我们还要多一个接收服务器的结构体呢？
                if (n >= 0)
                    buffer[n] = 0;

                std::cout << buffer << std::endl;
            }
            return nullptr;
        }
        void run()
        {
            pthread_create(&recver_,nullptr,sendMessage,(void*)&sockfd_);//参数只要sockfd
            pthread_detach(recver_);//——这个参数不是creat的返回值

            struct sockaddr_in serveraddr;
            memset(&serveraddr, 0, sizeof(serveraddr)); // 全部初始化为0
            serveraddr.sin_family = AF_INET;
            serveraddr.sin_port = htons(serverport_);
            serveraddr.sin_addr.s_addr = inet_addr(serverip_.c_str());

            std::string message;
            char cmdline[1024];
            // 写一个多线程！——让一部分进程来进行发送！一部分进行来进行收取！
            //主线程负责发送！线程负责读取！
            while(!quit_)
            {
                //  std::cout << "# ";
                fprintf(stderr, "Enter# ");
                fflush(stderr);
                fgets(cmdline, sizeof(cmdline), stdin);
                message = cmdline;
                message[message.size() - 1] = 0; // 去掉\n

                sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
            }
        }
        ~udpClient()
        {}
    private:
        int sockfd_;//clinet也需要一个套接字
        std::string serverip_;//服务器的ip地址
        uint16_t serverport_;//服务器的端口号
        bool quit_;//是否退出

        pthread_t recver_;
    };
}